//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------

#pragma once

#include <Common.h>

namespace Voxels
{

	//
	// Voxel Helpers
	//

	struct Helpers
	{
	// types
	public:
		enum {
			BORDER	= 1,
			COUNT	= BORDER * 2 + 1,
			COUNT2D	= COUNT * COUNT,
		};


	// methods
	public:

		// 3D to 2D coord //
		static void CalcTex2DSizeForBlock (OUT uvec2 &texSize, OUT uvec2 &numFrames,
											const uvec3 &blockSizeWithBorder, uint pixelPerVoxel, const uvec2 &maxTex2dSoze);

		static bool IsTex2DSizeValid (const uvec2 &texSize, const uvec2 &numFrames, const uvec3 &blockSizeWithBorder, uint pixelPerVoxel);

		static uvec2 Convert3DTo2D (const ivec3 &coord, const uvec2 &numFrames, const uvec3 &blockSize,
									const uvec3 &border, uint pixelPerVoxel);

		// Direction //
		static uint  DirectionToIndex (const ivec3 &dir);
		static ivec3 IndexToDirection (uint i);
	};



/*
=================================================
	CalcTex2DSizeForBlock
=================================================
*/
	inline void Helpers::CalcTex2DSizeForBlock (OUT uvec2 &texSize, OUT uvec2 &numFrames,
												const uvec3 &blockSizeWithBorder, uint pixelPerVoxel, const uvec2 &maxTex2dSize)
	{
		texSize			= uvec2(-1);
		numFrames		= uvec2(-1);

		uint64		square		= texSize.To<u64_vec2>().Square();
		const uvec3	size		= blockSizeWithBorder * uvec3( pixelPerVoxel, 1, 1 );
		const uint	layers_pot	= GetPOT( (uint) Floor( Sqrt( float( size.z ) ) ) );

		for (uint i = 0; i < layers_pot; ++i)
		{
			uvec2	num_layers	= uvec2( 1 << i, (uint) Ceil( float(size.z) / (1 << i) ) );	
			uvec2	tex_size	= size.xy() * num_layers;
			uint64	tex_square	= tex_size.To<u64_vec2>().Square();

			if ( All( tex_size < maxTex2dSize ) and tex_square < square )
			{
				texSize		= tex_size;
				numFrames	= num_layers;
				square		= tex_square;
			}
		}

		ASSERT( square != uint64(-1) );
		ASSERT( IsTex2DSizeValid( texSize, numFrames, blockSizeWithBorder, pixelPerVoxel ) );
	}
	
/*
=================================================
	IsTex2DSizeValid
=================================================
*/
	inline bool Helpers::IsTex2DSizeValid (const uvec2 &texSize, const uvec2 &numFrames, const uvec3 &blockSizeWithBorder, uint pixelPerVoxel)
	{
		const uvec3	size = blockSizeWithBorder * uvec3( pixelPerVoxel, 1, 1 );

		uvec2 c = Convert3DTo2D( blockSizeWithBorder.To<ivec3>()-1, numFrames, blockSizeWithBorder, uvec3(0), pixelPerVoxel );

		return All( c < texSize );
	}
	
/*
=================================================
	IsTex2DSizeValid
=================================================
*/
	inline uvec2 Helpers::Convert3DTo2D (const ivec3 &coord, const uvec2 &numFrames, const uvec3 &blockSize,
										 const uvec3 &border, uint pixelPerVoxel)
	{
		const uvec3	size = ( blockSize + border * 2 ) * uvec3( pixelPerVoxel, 1, 1 );
		const uvec3	in_c = Clamp( ( coord + ivec3(border) ) * ivec3( pixelPerVoxel, 1, 1 ), ivec3(0), size.To<ivec3>() ).To<uvec3>();
		const uvec2 c	 = uvec2( in_c.z % numFrames.x, in_c.z / numFrames.y ) * size.xy() + in_c.xy();
		return c;
	}
	
/*
=================================================
	DirectionToIndex
=================================================
*/
	inline uint Helpers::DirectionToIndex (const ivec3 &dir)
	{
		return (dir.x + BORDER) + ((dir.y + BORDER) * COUNT) + ((dir.z + BORDER) * COUNT * COUNT);
	}
	
/*
=================================================
	IndexToDirection
=================================================
*/
	inline ivec3 Helpers::IndexToDirection (uint i)
	{
		return ivec3( (i % COUNT) - BORDER, ((i / COUNT) % COUNT) - BORDER, (i / COUNT2D) - BORDER );
	}


}	// Voxels
